import { DomService, FormComponent, FormComponentType, WebCmdService } from '@farris/designer-services';
import { MessagerService } from '@farris/ui-messager';
import { DgControl } from '@farris/designer-ui';

/**
 * 表单保存前进行DOM结构有效性校验
 */
export class DomValidtionService {

    /** 表单完整DOM结构 */
    get domJson() {
        return this.domService.getDomJson();
    }

    constructor(
        private domService: DomService,
        private webCmdService: WebCmdService,
        private msgService: MessagerService) {

    }

    /**
     * 校验当前DOM结构：若返回提示信息，则阻止表单保存
     */
    checkDocumentValidBeforeSaved(): boolean {

        // 移除弹窗容器导入的组件声明中fileName和filePath为空的记录-----这个是以前的bug引起的无效数据
        let externalComponents = this.domService.getExternalComponent();
        externalComponents = externalComponents.filter(comp => {
            if (comp.type === DgControl.ModalContainer.type && comp.contentType === 'form' && !comp.fileName && !comp.filePath) {
                return false;
            }
            return true;
        });

        // 校验DOM中viewmodel和component是否一一对应。主要用于手动修改代码编辑器后，导致的编译失败问题
        if (!this.checkComponentAndViewModelCanMatch(this.domService.getDomJson())) {
            return false;
        }

        // 校验DOM中所有绑定事件是否有效
        if (!this.checkControlEventValid()) {
            return false;
        }

        // 校验ui控件的配置是否有效
        if (!this.checkUIComponentsValid()) {
            return false;
        }
        // 校验组件声明中的命令是否有效
        if (!this.checkDeclarationCommandValid()) {
            return false;
        }
        // 校验组件声明中的变量是否有效
        if (!this.checkDeclarationStateValid()) {
            return false;
        }
        return true;
    }

    /**
     * 校验控件绑定的命令是否有效
     */
    private checkControlEventValid(): boolean {

        const components = this.domJson.module.components.filter(c => !c.fakeDel);
        const findEvents = [];

        // 普通控件的按钮事件
        let allBoundEvents = this.webCmdService.findBoundEvent(components, findEvents, 'root-viewmodel');

        // 全局按钮的事件，例如Header、ModalFooter控件上的按钮
        if (this.domService.module.toolbar) {
            allBoundEvents = this.webCmdService.findBoundEventInToolbar(this.domService.module.toolbar, allBoundEvents);
        }
        // 去重
        allBoundEvents = this.webCmdService.getUniqueEvent(allBoundEvents);

        if (allBoundEvents.length) {
            for (const boundEvent of allBoundEvents) {
                const vm = this.domService.getViewModelById(boundEvent.viewModelId);
                let vmCommand;

                if (vm && vm.commands && vm.commands.length) {
                    vmCommand = vm.commands.find(c => c.code === boundEvent.commandLabel && !c.isInvalid);
                }
                if (!vmCommand) {
                    const controlInfo = this.domService.controlBasicInfoMap.get(boundEvent.id);
                    const controlName = (controlInfo && controlInfo.parentPathName) || boundEvent.id;
                    const msg = `控件【${controlName}】的【${boundEvent.eventName}】配置的命令已被移除，请重新选择。`;
                    this.msgService.warning(msg);
                    return false;
                }
            }
        }


        return true;

    }

    /**
     *  校验控件的配置是否有效
     */
    private checkUIComponentsValid() {
        return this.checkTreeGridValidation();

    }

    /**
     * 若树控件所在的组件配置了分层加载的命令，则树控件不支持启用多选
     */
    private checkTreeGridValidation() {
        const dataGridCmps = this.domService.components.filter(c => c.componentType === FormComponentType.dataGrid);
        if (dataGridCmps && dataGridCmps.length) {
            const loadByLevelCmps = dataGridCmps.filter(cmp => {
                if (!cmp.onInit) {
                    return false;
                }
                const viewmodel = this.domService.getViewModelById(cmp.viewModel);
                if (!viewmodel || !viewmodel.commands || !viewmodel.commands.length) {
                    return false;
                }
                const loadByLevelCommand = viewmodel.commands.find(command => command.code === cmp.onInit && command.handlerName === 'LoadByLevel');
                if (loadByLevelCommand) {
                    return true;
                }
                return false;
            });

            if (loadByLevelCmps && loadByLevelCmps.length) {
                const treeGridWithMultiSelect = loadByLevelCmps.filter(c => this.domService.selectNode(c, item => item.type === DgControl.TreeGrid.type && item.multiSelect));
                if (treeGridWithMultiSelect.length) {
                    this.msgService.warning('分层加载树勾选时无法勾选下级，请使用完全加载树或禁用多选！');
                    return false;

                }
            }
        }
        return true;
    }

    /**
     * 校验DOM中viewmodel和component是否一一对应。主要用于手动修改代码编辑器后，导致的编译失败问题
     */
    checkComponentAndViewModelCanMatch(domJson: any) {
        if (!domJson.module) {
            return true;
        }
        const { components, viewmodels } = domJson.module;
        const cmpIdList = [];
        for (const cmp of components) {
            if (cmpIdList.includes(cmp.id)) {
                this.msgService.showHtmlMsg('warning', '表单DOM结构错误', `id为【${cmp.id}】的component节点重复，请在代码编辑器中检查。`);
                return;
            }
            cmpIdList.push(cmp.id);
            const targetVM = viewmodels.find(vm => vm.id === cmp.viewModel);
            if (!targetVM) {
                this.msgService.showHtmlMsg('warning', '表单DOM结构错误', `id为【${cmp.id}】的component节点没有对应的viewmodel节点，请在代码编辑器中检查。`);
                return false;
            }
        }

        const vmIdList = [];
        for (const vm of viewmodels) {
            if (vmIdList.includes(vm.id)) {
                this.msgService.showHtmlMsg('warning', '表单DOM结构错误', `id为【${vm.id}】的viewmodel节点重复，请在代码编辑器中检查。`);
                return;
            }
            vmIdList.push(vm.id);
            const targetCmp = components.find(cmp => cmp.viewModel === vm.id);
            if (!targetCmp) {
                this.msgService.showHtmlMsg('warning', '表单DOM结构错误', `id为【${vm.id}】的viewmodel节点没有对应的component节点，请在代码编辑器中检查。`);
                return false;
            }

        }

        return true;
    }

    /**
     * 校验组件声明中的命令是否有效
     */
    private checkDeclarationCommandValid(): boolean {
        const declarations = this.domJson.module.declarations;
        if (!declarations) {
            return true;
        }
        const commands = declarations.commands || [];
        if (!commands.length) {
            return true;
        }

        for (const commandItem of commands) {
            const { name, command, path } = commandItem;
            if (name && command && path) {
                const pathArray = path.split('.');
                if (pathArray.length >= 2) {
                    const viewModelId = pathArray[pathArray.length - 2];

                    const viewModel = this.domService.getViewModelById(viewModelId);
                    if (!viewModel || !viewModel.commands || !viewModel.commands.length || !viewModel.commands.find(co => co.code === command)) {
                        const msg = `组件声明中的命令【${name}】已失效，请重新选择。`;
                        this.msgService.warning(msg);
                        return false;
                    }
                }
            }

        }

        return true;
    }

    /**
     * 校验组件声明中的变量是否有效
     */
    private checkDeclarationStateValid(): boolean {
        const declarations = this.domJson.module.declarations;
        if (!declarations) {
            return true;
        }
        const states = declarations.states || [];
        if (!states.length) {
            return true;
        }

        for (const stateItem of states) {
            const { name, state, path } = stateItem;
            if (name && state && path) {
                const pathArray = path.split('.');
                if (pathArray.length >= 2) {
                    const viewModelId = pathArray[pathArray.length - 2];

                    const viewModel = this.domService.getViewModelById(viewModelId);
                    if (!viewModel || !viewModel.states || !viewModel.states.length || !viewModel.states.find(co => co.code === state)) {
                        const msg = `组件声明中的变量【${name}】已失效，请重新选择。`;
                        this.msgService.warning(msg);
                        return false;
                    }
                }
            }
        }

        return true;
    }
}

